package divestoclimb.checklist;

import java.io.File;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedList;
import java.util.TimeZone;

import divestoclimb.util.ValidFilenameFilter;

import android.app.Activity;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Bundle;
import android.os.Environment;
import android.text.InputFilter;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;

/**
 * A dialog to prompt for a location and name of a file to save. Has its own
 * builtin database of recently used locations and allows picking one of those,
 * or launching a directory browser to pick a custom one.
 * @author Ben Roberts (divestoclimb@gmail.com)
 *
 */
public class SaveDialog extends Activity implements Button.OnClickListener {

	// These are the two parameters to pass in the Intent:
	// The suggested filename w/ extension
	public static final String PARAM_DEFAULT_NAME = "name";
	// The suggested location
	public static final String PARAM_LOCATION = "location";
	
	// This is the parameter that's returned in the result Intent
	public static final String PARAM_NAME = "name";
	
	private static final File DEFAULT_LOCATION = Environment.getExternalStorageDirectory();
	
	private String mName;
	private File mLocation;
	
	private EditText mNameField;
	private Spinner mLocationSpinner;
	private DatabaseHelper mDbHelper;
	private SQLiteDatabase mDb;
	private FilePickerHelper mFilePickerHelper;

	@Override
	protected void onCreate(Bundle icicle) {
		super.onCreate(icicle);
		setContentView(R.layout.save);
		mFilePickerHelper = new FilePickerHelper(this);

		Bundle params = icicle != null? icicle: getIntent().getExtras();
		if(params != null) {
			mName = params.getString(PARAM_DEFAULT_NAME);
			String location = params.getString(PARAM_LOCATION);
			try {
				if(location == null) {
					throw new Exception();
				}
				mLocation = new File(location);
				if(! mLocation.isDirectory() && ! mLocation.mkdirs()) {
					throw new Exception();
				}
			} catch(Exception e) {
				mLocation = DEFAULT_LOCATION;
			}
		}
		if(mLocation == null) {
			mLocation = DEFAULT_LOCATION;
		}
		
		mNameField = (EditText)findViewById(R.id.name);
		ArrayList<InputFilter> filters = new ArrayList<InputFilter>(Arrays.asList(mNameField.getFilters()));
		filters.add(new ValidFilenameFilter());
		filters.add(new InputFilter.LengthFilter(255));	// max file name size on FAT32
		InputFilter[] filtersif = new InputFilter[filters.size()];
		for(int i = 0; i < filters.size(); i++) {
			filtersif[i] = filters.get(i);
		}
		mNameField.setFilters(filtersif);
		mLocationSpinner = (Spinner)findViewById(R.id.recent_locations);
		mLocationSpinner.setPrompt(getString(R.string.recent_locations));
		
		Button choose_location = (Button)findViewById(R.id.choose_location),
				cancel = (Button)findViewById(R.id.button_cancel),
				save = (Button)findViewById(R.id.button_save);
		choose_location.setOnClickListener(this);
		cancel.setOnClickListener(this);
		save.setOnClickListener(this);
	}
	
	public void onPause() {
		super.onPause();
		mDb.close();
	}
	
	public void onResume() {
		super.onResume();
		mDbHelper = new DatabaseHelper(this);
		mDb = mDbHelper.getWritableDatabase();
		mNameField.setText(mName);

		LinkedList<String> locations = new LinkedList<String>();
		int defaultPosition = 0;
		String path = null;
		try {
			path = mLocation.getCanonicalPath();
		} catch(IOException e) {
			// We're validating every time we set mLocation so this is not possible.
		}
		if(getLocationId(path) == -1) {
			locations.add(path);
		}
		Cursor recent_locations = fetchLocations(5);
		while(recent_locations.moveToNext()) {
			String name = recent_locations.getString(recent_locations.getColumnIndexOrThrow(KEY_LOCATION_PATH));
			locations.add(name);
			if(name == path) {
				defaultPosition = locations.size() - 1;
			}
		}
		
		ArrayAdapter<String> spinadapter = new ArrayAdapter<String>(this,
				android.R.layout.simple_spinner_item, android.R.id.text1, locations);
		spinadapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
		mLocationSpinner.setAdapter(spinadapter);
		mLocationSpinner.setSelection(defaultPosition);
	}

	@Override
	public void onSaveInstanceState(Bundle outState) {
		super.onSaveInstanceState(outState);
		
		outState.putString(PARAM_DEFAULT_NAME, mNameField.getText().toString());
		outState.putString(PARAM_LOCATION, (String)mLocationSpinner.getSelectedItem());
	}
	
	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		super.onActivityResult(requestCode, resultCode, data);
		File loc = mFilePickerHelper.getFileFromResult(requestCode, resultCode, data);
		if(loc != null) {
			mLocation = loc;
		}
	}

	private String getExtension(String filename) {
		return filename.substring(filename.lastIndexOf(".")+1);
	}

	public void onClick(View v) {
		Intent i = new Intent();
		switch(v.getId()) {
		case R.id.choose_location:
			mFilePickerHelper.selectDirectory(new File((String)mLocationSpinner.getSelectedItem()),
					getString(R.string.choose_location), new String[] { getExtension(mName) });
			break;
		case R.id.button_save:
			// Save the current location back to the recent locations database
			String location = (String)mLocationSpinner.getSelectedItem();
			saveLocation(location);
			Bundle b = new Bundle();
			b.putString(PARAM_NAME, location + File.separator + mNameField.getText().toString());
			i.putExtras(b);
			setResult(RESULT_OK, i);
		case R.id.button_cancel:
			finish();
		}
	}
	
	// The database for recent save locations
	private static final String TABLE_LOCATIONS = "locations";
	private static final String KEY_LOCATION_ID = "_id";
	private static final String KEY_LOCATION_PATH = "path";
	private static final String KEY_LOCATION_LASTUSED = "lastUsed";
	
	protected static final DateFormat mDateFormatGMT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	static {
		mDateFormatGMT.setTimeZone(TimeZone.getTimeZone("GMT"));
	}
	
	private static class DatabaseHelper extends SQLiteOpenHelper {
		private static final String TAG = "Save Locations DB";

		private static final String DATABASE_NAME="savelocations";
		public static final int DATABASE_VERSION=1;

		public DatabaseHelper(Context ctx) {
			super(ctx, DATABASE_NAME, null, DATABASE_VERSION);
		}

		@Override
		public void onCreate(SQLiteDatabase db) {
			onUpgrade(db, 0, DATABASE_VERSION);
		}
		
		@Override
		public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
			int v = oldVersion;
			if(v <= 1) {
				Log.w(TAG, "Upgrading database from version " + v + " to " + newVersion + ", which" +
					"will destroy all old data");
				db.execSQL("DROP TABLE IF EXISTS locations");
				db.execSQL("create table locations ("
						+"_id integer primary key autoincrement, "
						+"path text not null, "
						+"lastUsed text check (lastUsed LIKE '____-__-__ __:__:__')"
					+")");
				v = 1;
			}
		}
	}

	/**
	 * Get a Cursor of the most recently used locations
	 * @param limit The maximum number of locations to retrieve
	 * @return A Cursor of locations
	 */
	private Cursor fetchLocations(Integer limit) {
		return mDb.query(TABLE_LOCATIONS,
				new String[] { KEY_LOCATION_ID, KEY_LOCATION_PATH }, null, null, null, null,
				KEY_LOCATION_LASTUSED + " DESC", limit.toString());
	}

	/**
	 * Get the ID of the database record corresponding to the given location path.
	 * @param location The location to get the ID for
	 * @return The ID of this location, or -1 if there is no record that matches.
	 */
	private long getLocationId(String location) {
		Cursor c = mDb.query(TABLE_LOCATIONS, new String[] { KEY_LOCATION_ID }, KEY_LOCATION_PATH + "=?",
				new String[] { location }, null, null, null);
		if(c != null && c.getCount() > 0) {
			c.moveToFirst();
			long id = c.getLong(c.getColumnIndexOrThrow(KEY_LOCATION_ID));
			c.close();
			return id;
		} else {
			return -1;
		}
	}

	/**
	 * Save a location to the database. If the same path already exists, the last time
	 * it was used is updated. Otherwise, a new record is created.
	 * @param location 
	 */
	private void saveLocation(String location) {
		Long id = getLocationId(location);
		ContentValues v = new ContentValues();
		v.put(KEY_LOCATION_LASTUSED, mDateFormatGMT.format(new Date()));
		if(id == -1) {
			v.put(KEY_LOCATION_PATH, location);
			mDb.insert(TABLE_LOCATIONS, null, v);
		} else {
			mDb.update(TABLE_LOCATIONS, v, KEY_LOCATION_ID + "=?", new String[] { id.toString() });
		}
	}
}